'''
Created on Mar 23, 2013

@author: Petko
'''
from django.views.generic.base import View
from sent.utils import createValidToken, CommonResult, ErrorIds, AccessToken
from sent.utils.result_printer import getPrinter
from sent.model.entities.opinion import Opinion, OpinionEvaluations,\
    OpinionState, PendingUpdateState
from sent.model.OpinionDBManager import OpinionDBManager

from sent.utils.attention import Deltas
from sent.opinion import extract_path_to_base, change_att_deltas_to_base,\
    neutralize_non_neutral_opinion
from httplib import BAD_REQUEST
from sententialyzer.settings import DEBUG
import time
from sent.utils.commit_scheduler import g_commit_scheduler
from uuid import UUID

class CreateOpinion(View):
    '''
    classdocs
    '''
    
    def __init__(self):
        '''
        Constructor
        '''
        pass
    
    def post(self, *args, **kwargs):
        accessTokenStr = self.request.POST.get('access_token', None)
        parent = self.request.POST.get('parent', None)
        base_op_id = self.request.POST.get('base_op_id', None)
        
        title = self.request.POST.get('title', '')
        content = self.request.POST.get('content', '')
        opinion_data = self.request.POST.get('opinion_data', '')
        evaluation = self.request.POST.get('evaluation', '')
        include_path_to_base = self.request.POST.get('include_path_to_base', False)
        
        commit_delay=0
        if DEBUG:
            commit_delay = float(self.request.POST.get('commit_delay', 0))
            
        include_path_to_base = include_path_to_base and include_path_to_base != "0"
        
        common_res = CommonResult()
        
        if accessTokenStr and evaluation and parent:
            
            opinion = Opinion()
            opinion.evaluation = OpinionEvaluations.STRING_TO_EVAL.get(evaluation, None)
            
            if opinion.evaluation:
                if opinion.evaluation != OpinionEvaluations.NEUTRAL or title or content:
                    dbman = OpinionDBManager(False)
                    try:
                        accessToken, error = createValidToken(accessTokenStr)
                        
                        if base_op_id:
                            base_op_id = UUID(base_op_id).bytes
                        else:
                            base_op_id = dbman.get_base_op_id_str(parent)
                        
                        if base_op_id:
                            #lock the base opinion so that commit of pending operations wait for this operation 
                            pending_ud = dbman.lock_base_op_ro(base_op_id)
                            
                            if accessToken and opinion.evaluation != OpinionEvaluations.NEUTRAL:
                                #lock the user so that the user cannot create 2 votes via concurrent queries
                                dbman.lock_user(accessToken.getOwnerUUID())
                            
                            parent_op = dbman.get_opinion_by_id_str(parent, \
                                                                    allowed_states=[OpinionState.NORMAL, OpinionState.WAITING_UPDATE])
                            
                            if parent_op:
                                base_op_id = parent_op.base_op_id
                                if parent_op.has_content:
                                    assert isinstance(parent_op, Opinion)
                                    
                                    if accessToken:
                                        assert isinstance(accessToken, AccessToken)
                                        if accessToken.containsScope(str(parent_op.getBaseUUID()) + ':c') \
                                            or ( accessToken.containsScope('all:c') \
                                                 and parent_op.containsDefaultScope('c') ):
                                            
                                            opinion.setCreatorIdString(accessToken.owner_id)
                                            
                                            opinion.external_data = unicode(opinion_data)
                                            opinion.title = unicode(title)
                                            opinion.textData = unicode(content)
                                            
                                            deltas = Deltas()
                                            
                                            common_res.result = {}
                                            common_res.result['impact'] = {}
                                            
                                            
                                            
                                            if pending_ud:
                                                dbman.expunge(pending_ud)
                                            #createOpinion = True
                                            
                                            if opinion.evaluation != OpinionEvaluations.NEUTRAL:
                                                
                                                non_neutral_op = dbman.get_non_neutral_op_per_usr_bop(opinion.creator_id, parent_op.base_op_id)
                                                
                                                opinion.has_content = len(opinion.title) != 0 or len(opinion.textData) != 0
                                                
                                                opinions_to_update = dbman.get_path_to_opinions_safe(False, parent_op, non_neutral_op)
                                                
                                                impact = neutralize_non_neutral_opinion(non_neutral_op, opinions_to_update, deltas, dbman)
                                                common_res.result['impact'].update(impact)
                                                
                                                path_found, path_to_op = extract_path_to_base(parent_op, opinions_to_update)
                                                
                                                if include_path_to_base:
                                                    common_res.result['path_to_base'] = [path_op.toPrimitiveDict() for path_op in path_to_op]
                                                    
                                                assert path_found
                                                
                                                assert isinstance(path_to_op, list)
                                                
                                                path_to_op.insert(0, parent_op)
                                                
                                                if opinion.evaluation == OpinionEvaluations.POSITIVE:
                                                    change_att_deltas_to_base(deltas, path_to_op, 1, 0)
                                                elif opinion.evaluation == OpinionEvaluations.NEGATIVE:
                                                    change_att_deltas_to_base(deltas, path_to_op, 0, 1)
                                            
                                            deltas_list = [delta for delta in deltas.getAllDeltas()]
                                            #dbman.applyAttentionDeltas(deltas_list)
                                            
                                            #http://www.sitepoint.com/hierarchical-data-database-2/
                                            
                                            dbman.createOpinion(opinion, parent_op)
                                            common_res.result['opinion'] = opinion.toPrimitiveDict()
                                                
                                            common_res.result['impact']['attention_deltas'] = [delta.toPrimitiveDict() for delta in deltas_list]
                                            
                                            if commit_delay>0:
                                                time.sleep(commit_delay)
                                            
                                            dbman.commitSession()
                                            
                                            if not pending_ud or pending_ud.state == PendingUpdateState.IDLE:
                                                dbman.set_base_op_for_update(base_op_id)
                                                dbman.commitSession()
                                            
                                            g_commit_scheduler.schedule_commit()
                                            
                                            common_res.errorId = ErrorIds.SUCCESS
                                        
                                        else:
                                            common_res.errorId = ErrorIds.UNAUTHORIZED
                                            common_res.result = "No 'c' permissions for this base opinion"
                                    else:
                                        common_res.errorId = ErrorIds.UNAUTHORIZED
                                        common_res.result = error
                                else:
                                    common_res.errorId = BAD_REQUEST
                                    common_res.result = "Parent opinion contains no valuable content"
                            else:
                                common_res.errorId = ErrorIds.NOT_FOUND
                                common_res.result = "Parent %s not found" % parent
                        else:
                            common_res.errorId = ErrorIds.NOT_FOUND
                            common_res.result = "Parent %s not found" % parent
                    except:
                        dbman.rollbackSession()
                        
                        raise
                    finally:
                        if common_res.errorId != ErrorIds.SUCCESS:
                            dbman.rollbackSession()
                        dbman.closeSession()
                else:
                    common_res.errorId = ErrorIds.BADREQUEST
                    common_res.result = "Either content, or evaluation is expected"
            else:
                common_res.errorId = ErrorIds.BADREQUEST
                common_res.result = "Unknown evaluation %s" % evaluation
        else:
            common_res.errorId = ErrorIds.BADREQUEST
            common_res.result = "Missing parameter"
            
        return getPrinter().printResult(common_res)
    
    